#include "engine.h"
#include <random>

Engine::Engine(int rows, int cols) : grid(rows, cols), state(EngineState::Idle), steps_per_tick(1), algo_id(4) {}

void Engine::setAlgorithm(std::unique_ptr<Algorithm> a) { algo = std::move(a); resetAlgo(); }

void Engine::setAlgoId(int id) { algo_id = id; }

int Engine::getAlgoId() const { return algo_id; }

const char* Engine::algoName() const {
    switch(algo_id) {
        case 1: return "BFS";
        case 2: return "DFS";
        case 3: return "Dijkstra";
        case 4: return "A*";
        case 5: return "GBFS";
        case 6: return "Bi-BFS";
    }
    return "BFS";
}

void Engine::setDiagonal(bool v) { grid.diagonal = v; }

void Engine::setStart(Pos p) { grid.start = p; }

void Engine::setGoal(Pos p) { grid.goal = p; }

Grid& Engine::getGrid() { return grid; }

const AlgoState& Engine::getAlgoState() const { return last; }

Stats Engine::getStats() const {
    Stats s; s.steps = 0; s.visited = (int)last.visited.size(); s.path_len = (int)last.path.size(); return s;
}

void Engine::clearObstacles() { std::fill(grid.cells.begin(), grid.cells.end(), 0); }

void Engine::randomizeObstacles(double ratio, unsigned int seed) {
    if (ratio < 0) ratio = 0; if (ratio > 0.95) ratio = 0.95;
    std::mt19937 rng(seed);
    std::uniform_real_distribution<double> distu(0.0, 1.0);
    for (int r=0;r<grid.rows;r++) {
        for (int c=0;c<grid.cols;c++) {
            Pos p{r,c};
            bool block = distu(rng) < ratio;
            grid.set_block(p, block);
        }
    }
    grid.set_block(grid.start, false);
    grid.set_block(grid.goal, false);
}

void Engine::toggleRun() { state = state==EngineState::Running ? EngineState::Paused : EngineState::Running; }

void Engine::pause() { state = EngineState::Paused; }

void Engine::resetAlgo() { if (algo) { algo->reset(grid); last = algo->state(); state = EngineState::Paused; } }

void Engine::tick() {
    if (state != EngineState::Running) return;
    for (int i=0;i<steps_per_tick;i++) { if (algo) { algo->step(grid); last = algo->state(); if (last.finished) { state = EngineState::Paused; break; } } }
}

void Engine::stepOnce() { if (algo) { algo->step(grid); last = algo->state(); if (last.finished) state = EngineState::Paused; } }

void Engine::setStepsPerTick(int n) { steps_per_tick = n<1?1:n; }

